package com.lty.redis;

import com.lty.util.GsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * redis工具类
 *
 * @author lty
 */
@Slf4j
@Component
public class RedisUtil {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 写入缓存
     *
     * @param key
     * @param value
     * @return
     */
    public <T> boolean set(final String key, T value) {
        boolean result = false;
        try {
            String jsonValue = value instanceof String ? (String) value : GsonUtil.beanToJson(value);
            stringRedisTemplate.opsForValue().set(key, jsonValue);
            result = true;
        } catch (Exception e) {
            log.error("Redis set error: ", e);
        }
        return result;
    }

    /**
     * 写入缓存设置时效时间(默认为秒)
     *
     * @param key
     * @param value
     * @return
     */
    public <T> boolean set(final String key, T value, Long expireTime) {
        boolean result = false;
        try {
            String jsonValue = value instanceof String ? (String) value : GsonUtil.beanToJson(value);
            stringRedisTemplate.opsForValue().set(key, jsonValue);
            if (expireTime != null) {
                stringRedisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            }
            result = true;
        } catch (Exception e) {
            log.error("Redis set error: ", e);
        }
        return result;
    }

    /**
     * 读取缓存
     *
     * @param key
     * @return
     */
    public String get(final String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    /**
     * 判断缓存中key是否存在
     *
     * @param key
     * @return
     */
    public boolean exists(final String key) {
        return stringRedisTemplate.hasKey(key);
    }

    /**
     * 获取有效时间
     *
     * @param key Redis键
     * @return 有效时间
     */
    public long getExpire(final String key) {
        return stringRedisTemplate.getExpire(key);
    }

    /**
     * 返回 key 的剩余的过期时间
     * @param key
     * @param unit
     * @return
     */
    public Long getExpire(String key, TimeUnit unit) {
        return stringRedisTemplate.getExpire(key, unit);
    }

    /**
     * 删除对应的value
     *
     * @param key
     */
    public void remove(final String key) {
        if (exists(key)) {
            stringRedisTemplate.delete(key);
        }
    }

    /**
     * 批量删除对应的value
     *
     * @param keys
     */
    public void remove(final String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }

    /**
     * 使用 SCAN 命令删除与给定模式匹配的 Redis 键。
     *
     * @param pattern Redis 键的模式 (支持通配符，例如 project:business:*)
     * @return 删除的键数量
     */
    public long deleteKeysByPattern(String pattern) {
        if (pattern == null || pattern.isEmpty()) {
            throw new IllegalArgumentException("pattern 不能为空");
        }

        Set<String> keysToDelete = new HashSet<>();

        // 使用 SCAN 命令分批扫描匹配的键
        Cursor<byte[]> cursor = stringRedisTemplate.getConnectionFactory()
                .getConnection()
                .scan(ScanOptions.scanOptions().match(pattern).count(1000).build());

        while (cursor.hasNext()) {
            keysToDelete.add(new String(cursor.next()));
        }

        if (!keysToDelete.isEmpty()) {
            stringRedisTemplate.delete(keysToDelete);
        }

        return keysToDelete.size();
    }

    /**
     * 获取所有key
     *
     * @return
     */
    public Set<String> getAll() {
        Set<String> keys = stringRedisTemplate.keys("*");
        return keys;
    }

    /**
     * 清除所有key
     */
    public void flush() {
        Set<String> keys = stringRedisTemplate.keys("*");
        keys.forEach(e -> remove(e));
    }

    public static long generate(RedisTemplate<?, ?> redisTemplate, String key, Date expireTime) {
        if (redisTemplate == null || key == null || key.isEmpty()) {
            throw new IllegalArgumentException("RedisTemplate and key should not empty");
        }
        //RedisAtomLong 为原子类，根据传入的key
        RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
        //设置过期时间
        if (null != expireTime) {
            counter.expireAt(expireTime);
        }
        return counter.incrementAndGet();
    }

    /**
     * 生成业务唯一递增id(格式为[prefix]2023082900000001)
     *
     * @param key    业务标识
     * @param prefix 业务头标识
     * @notice 注意服务器重启后,业务号可能会重复,最好把默认值调大后使用
     */
    public String getBusinessId(String key, String prefix) {
        if (key == null || key.isEmpty()) {
            throw new IllegalArgumentException("Business identification key 不能为空");
        }
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        //设置过期时间，这里设置为当天的23:59:59
        Date expireDate = calendar.getTime();
        //返回当前redis中key的最大值
        Long businessId = generate(stringRedisTemplate, key, expireDate);

        //获取当天的日期，格式为yyyyMMdd
        String date = new SimpleDateFormat("yyyyMMdd").format(expireDate);

        //生成八位的序列号，如果seq不为空，则seq前面补0;
        //如果seq位数超过了八位，那么无需补0直接返回当前的seq
        String sequence = StringUtils.leftPad(businessId.toString(), 8, "0");
        // 确保 prefix 非空
        prefix = (prefix != null) ? prefix : "";
        //拼接业务号
        String sequenceId = prefix + date + sequence;
        return sequenceId;
    }
}
